﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.IO;
using System.Diagnostics;
using System.Reflection;

namespace Sinacor.OMM.DumpServer
{
    public class Tools
    {

        public class Sleeper
        {

            public delegate bool HandlerToAbortSleep();

            public static void Sleep(int miliseconds, HandlerToAbortSleep handlerToAbortSleep)
            {
                new SleepCaps().SleepInten(miliseconds, handlerToAbortSleep);
            }

            private class SleepCaps
            {
                public void SleepInten(int miliseconds, HandlerToAbortSleep handlerToAbortSleep)
                {
                    const int MAX_TIME_TO_THREAD_SLEEP = 1000;

                    int totalTimeToSleep = miliseconds;
                    int fractionedTimeToSleep;
                    int timeSlept;

                    if (totalTimeToSleep <= 0)
                        throw new ArgumentOutOfRangeException("Não pode haver Sleep(0)");

                    fractionedTimeToSleep = totalTimeToSleep % MAX_TIME_TO_THREAD_SLEEP;

                    timeSlept = 0;
                    while ((timeSlept < totalTimeToSleep) && handlerToAbortSleep())
                    {
                        if (fractionedTimeToSleep > 0)
                        {
                            Thread.Sleep(fractionedTimeToSleep);
                            timeSlept += fractionedTimeToSleep;
                            fractionedTimeToSleep = 0;
                        }
                        else
                        {
                            Thread.Sleep(MAX_TIME_TO_THREAD_SLEEP);
                            timeSlept += MAX_TIME_TO_THREAD_SLEEP;
                        }
                    }
                }
            }
        }

        public class TraceWriter
        {
            private const string CLSID = "C96D15EF-4DC6-4c4b-942E-38216D0EB3D9";

            private string _path;
            private string _fileName;
            private bool _closing = false;
            private Mutex _mutex = new Mutex();
            private bool _optionalTraceEnabled = false;
            private FileStream fs;

            public TraceWriter(string folderName, string fileName, string clsId)
            {
                if (clsId != CLSID)
                    throw new InvalidOperationException("TraceWriter não pode ser instanciado diretamente, use Tracer.GetInstance()");

                try
                {
                    this._path = folderName;
                    this._fileName = fileName;

                    if (_path.StartsWith(@"\"))
                        _path = _path.Substring(1, _path.Length - 1);
                    if (!String.IsNullOrEmpty(_path.Trim()))
                    {
                        if (_path.Substring(1, 1) == @":")
                            _path = _path.Substring(3, _path.Length - 3);
                    }

                    _path = DeterminaPath() + folderName;

                    if (!_path.EndsWith(@"\"))
                        _path += @"\";

                    if (!_fileName.EndsWith(@".log"))
                    {
                        _fileName += DateTime.Now.ToString(" yyyy-dd-MM");
                        _fileName += @".log";
                    }
                    else // Se termina com .log
                    {
                        _fileName = _fileName.Substring(0, _fileName.Length - 4);
                        _fileName += DateTime.Now.ToString(" yyyy-dd-MM");
                        _fileName += @".log";
                    }

                    if (_fileName.Contains(":"))
                        _fileName = _fileName.Replace(":", "-");

                    if (IsFileOpen(Path.Combine(_path, _fileName)))
                    {
                        _fileName = _fileName + " Concurrency Safe " + DateTime.Now.ToString("hhMMssffff") + ".log";
                        if (IsFileOpen(Path.Combine(_path, _fileName)))
                        {
                            _fileName = _fileName + " Error Safe " + DateTime.Now.ToString("hhMMssffff") + " " + Process.GetCurrentProcess().Id + Thread.CurrentThread.ManagedThreadId + ".log";
                        }
                    }

                }
                catch (System.IO.IOException ex)
                {
                    LogEventViewer("OMM", "TraceWriter", ex.StackTrace, EventLogEntryType.Error, 9999);
                }
                catch (Exception ex)
                {
                    LogEventViewer("OMM", "TraceWriter", ex.StackTrace, EventLogEntryType.Error, 9999);
                }

            
                fs = new FileStream(_path + _fileName, FileMode.Append);
            }

            private string DeterminaPath()
            {
                string rootPath = DumpConfiguration.DumpConfigurationManager.DumperConfiguration.RootPath;
                string date = DateTime.Now.ToString("yyyyMMdd");

                if (!rootPath.EndsWith(@"\"))
                    rootPath += @"\";

                rootPath += date + @"\";

                if (!_path.EndsWith(@"\"))
                    _path = rootPath + _path + @"\";

                if (!Directory.Exists(_path))
                    Directory.CreateDirectory(_path);

                return rootPath;
            }

            public void Trace(string expression)
            {
                if (!_closing && fs != null)
                {
                    _mutex.WaitOne();
                    lock (fs)
                    {
                        expression = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff") + " -> " + expression + "\r\n";
                        try
                        {
                            fs.Write(Encoding.Default.GetBytes(expression), 0, expression.Length);
                            fs.Flush();
                        }
                        catch (Exception)
                        {
                            try
                            {
                                FileStream fsBackup = new FileStream(_path + "lost" + _fileName, FileMode.OpenOrCreate);
                                fsBackup.Write(Encoding.Default.GetBytes(expression), 0, expression.Length);
                                fsBackup.Flush();
                                fsBackup.Close();
                                fsBackup.Dispose();
                            }
                            catch (Exception ex)
                            {
                                LogEventViewer("OMM", "TraceWriter", expression + "\r\n" + ex.StackTrace, EventLogEntryType.Error, 9999);
                            }
                        }
                    }
                    _mutex.ReleaseMutex();
                }
            }

            public void Trace(string expression, TraceType traceType)
            {
                if (traceType == TraceType.Optional)
                {
                    if (_optionalTraceEnabled)
                    {
                        Trace(expression);
                    }
                }
            }

            public void Close()
            {
                try
                {

                    _closing = true;


                    if (fs != null)
                    {
                        _mutex.WaitOne();
                        lock (fs)
                        {
                            fs.Flush();
                            fs.Close();
                            fs.Dispose();
                        }
                        _mutex.ReleaseMutex();
                        _mutex.Close();

                    }
                }
                catch (Exception ex)
                {
                    LogEventViewer("OMM", "TraceWriter", ex.StackTrace, EventLogEntryType.Error, 9999);
                }

            }

            public void LogEventViewer(string sSource, string sLog, string sEvent, EventLogEntryType eType, int eventid)
            {
                if (!EventLog.SourceExists(sSource))
                    EventLog.CreateEventSource(sSource, sLog);

                EventLog.WriteEntry(sSource, sEvent);
                EventLog.WriteEntry(sSource, sEvent, eType, eventid);
            }

            public bool OptionalTraceEnabled
            {
                get { return _optionalTraceEnabled; }
                set { _optionalTraceEnabled = value; }
            }

            private bool IsFileOpen(string filePath)
            {
                bool fileOpened = false;
                
                try
                {
                    System.IO.FileStream fs = System.IO.File.OpenWrite(filePath);
                    fs.Close();
                    fs.Dispose();
                }
                catch (System.IO.IOException ex)
                {
                    fileOpened = true;
                }
                catch (Exception ex)
                {
                    fileOpened = true;
                }

                return fileOpened;
            }

        }

        public enum TraceType
        {
            Mandatory,
            Optional
        }

        public class Tracer
        {
            private const int TIME_TO_CHECK_OPTIONAL_TRACE_ENABLED = 2000;
            private const string CLSID = "C96D15EF-4DC6-4c4b-942E-38216D0EB3D9";

            private static Dictionary<string, Tools.TraceWriter> _dctLogServer;
            private static bool _closing;
            private static bool _optionalTraceEnabled;

            private Tracer()
            {
                _optionalTraceEnabled = OptionalTraceEnabledFileRead();
            }

            public static Tools.TraceWriter GetInstance(string folderName, string filename)
            {
                if (!_closing)
                {
                    try
                    {

                        if (_dctLogServer == null)
                        {
                            _dctLogServer = new Dictionary<string, Tools.TraceWriter>();
                            Thread thr = new Thread(new ThreadStart(OptionalTraceEnabledMonitor));
                            thr.Name = "Monitora trace habilitado";
                            thr.Start();
                        }

                        lock (_dctLogServer)
                        {
                            Tools.TraceWriter ret;
                            if (_dctLogServer.ContainsKey(folderName + filename))
                                ret = _dctLogServer[folderName + filename];
                            else
                            {
                                ret = new Tools.TraceWriter(folderName, filename, CLSID)
                                {
                                    OptionalTraceEnabled = _optionalTraceEnabled
                                };
                                _dctLogServer.Add(folderName + filename, ret);
                            }
                            return ret;
                        }
                    }
                    catch (Exception ex)
                    {
                        EventLog.WriteEntry("Tracer", ex.Message);
                        EventLog.WriteEntry("Tracer", ex.StackTrace);
                        return null;
                    }
                }
                else
                    return null;
            }

            private static void OptionalTraceEnabledMonitor()
            {

                bool lastOptionalTraceEnabled = false;

                while (!_closing)
                {
                    try
                    {
                        _optionalTraceEnabled = OptionalTraceEnabledFileRead();

                        if (lastOptionalTraceEnabled != _optionalTraceEnabled)
                        {
                            if (_dctLogServer != null)
                            {
                                lock (_dctLogServer)
                                {
                                    foreach (Tools.TraceWriter logWriter in _dctLogServer.Values)
                                    {
                                        logWriter.OptionalTraceEnabled = _optionalTraceEnabled;
                                    }
                                }
                            }
                        }

                        lastOptionalTraceEnabled = _optionalTraceEnabled;
                        Tools.Sleeper.Sleep(TIME_TO_CHECK_OPTIONAL_TRACE_ENABLED, () => !_closing);
                    }
                    catch (Exception ex)
                    {
                        EventLog.WriteEntry("Tracer", ex.Message);
                        EventLog.WriteEntry("Tracer", ex.StackTrace);
                    }
                }
            }

            private static bool OptionalTraceEnabledFileRead()
            {
                string FILE_NAME = string.Concat(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.DirectorySeparatorChar, "Trace.ini");
                const string TAGNAME = "TraceEnabled";

                bool ret;

                try
                {
                    if (!File.Exists(FILE_NAME))
                    {
                        File.WriteAllText(FILE_NAME, TAGNAME + "=0");
                        ret = false;
                    }
                    else
                    {
                        try
                        {

                            string fileContent = File.ReadAllText(FILE_NAME);
                            int idxValorTraceEnabled = fileContent.IndexOf(TAGNAME) + TAGNAME.Length;
                            string value = fileContent.Substring(idxValorTraceEnabled, fileContent.Length - idxValorTraceEnabled);
                            value = value.Replace(" ", string.Empty);
                            value = value.Replace("=", string.Empty);
                            value = value.Replace("\r\n", string.Empty);
                            if (int.Parse(value) == 0)
                                ret = false;
                            else
                                ret = true;
                        }
                        catch (Exception ex)
                        {
                            if (!File.Exists(FILE_NAME))
                            {
                                File.Delete(FILE_NAME);
                                File.WriteAllText(FILE_NAME, TAGNAME + "=0");
                            }
                            ret = false;
                        }
                    }
                }
                catch (Exception ex)
                {
                    EventLog.WriteEntry("Tracer", "Não conseguiu ler arquivo de Trace");
                    EventLog.WriteEntry("Tracer", "Arquivo: [" + FILE_NAME + "]");
                    EventLog.WriteEntry("Tracer", ex.Message);
                    EventLog.WriteEntry("Tracer", ex.StackTrace);
                    ret = false;
                }

                return ret;
            }

            public static void RemoveInstance(string folderName, string filename)
            {
                try
                {
                    if (_dctLogServer != null)
                    {
                        lock (_dctLogServer)
                        {
                            _dctLogServer[folderName + filename].Close();
                            _dctLogServer.Remove(folderName + filename);
                        }
                    }
                }
                catch (Exception ex)
                {
                    EventLog.WriteEntry("Tracer", ex.Message);
                    EventLog.WriteEntry("Tracer", ex.StackTrace);
                }
            }

            public static void Destroy()
            {
                try
                {
                    _closing = true;

                    if (_dctLogServer != null)
                    {
                        lock (_dctLogServer)
                        {
                            foreach (Tools.TraceWriter logWriter in _dctLogServer.Values)
                            {
                                logWriter.Close();
                            }
                            _dctLogServer.Clear();
                        }
                    }
                }
                catch (Exception ex)
                {
                    EventLog.WriteEntry("Tracer", ex.Message);
                    EventLog.WriteEntry("Tracer", ex.StackTrace);
                }
            }
        }
    }
}

